/** ****************************************************************************
 * @file 	modbus.c
 * @author 	mojinpan
 * @copyright (c) 2022-20xx, mojinpan All rights reserved.
 * @brief 	modbus协议组件
 *
 * @version 	V1.0
 * @date 	2024-2-16
 * @details
 * 1.初步实现modbus主机通讯功能
 * 2.提供PDU,RTU,TCP三种通讯模式
 * 3.默认支持功能码 01 02 03 04 05 06 15 16 22
 * 4.支持功能码的自定义扩展
 * 5.支持寄存器读写和指定功能码调用两种操作方式
 * 6.支持缓存模式自定义,可根据需要使用静态缓存或动态缓存
 * 7.支持调用方式自定义,根据收发函数的实现不同,可分别按阻塞和非阻塞两种方式调用
 * 
 * @version 	V1.1
 * @date 	2024-2-17
 * @details
 * 1.进一步增加对modbus从机的支持
 *******************************************************************************/
#include "modbus.h"

/*------------------------------------------------------------------------------
Macro Definition 宏定义
------------------------------------------------------------------------------*/
#define MODBUS_PDU_MIN              1       // 最小PDU报文长度

#define MODBUS_RTU_ADU_MIN          4       // RTU中最小ADU报文长度
#define MODBUS_RTU_ADU_PADDING      3       // RTU中添加到PDU的额外字节数
#define MODBUS_RTU_PDU_OFFSET       1       // PDU在RTU中帧的偏移

#define MODBUS_TCP_ADU_MIN          8       // TCP中最小ADU报文长度
#define MODBUS_TCP_ADU_PADDING      7       // TCP中添加到PDU的额外字节数
#define MODBUS_TCP_PDU_OFFSET       7       // PDU在TCP中帧的偏移

// 将bit转换成byte
#define MODBUS_BITS_TO_BYTES(n) (((n) + 7) >> 3)
/*------------------------------------------------------------------------------
Function declaration 函数声明
------------------------------------------------------------------------------*/
int modbusBuildException(ModbusSlave_t *obj,uint8_t func,int code);
/*------------------------------------------------------------------------------
Global Variables 全局变量
------------------------------------------------------------------------------*/


/** ****************************************************************************
 * @brief  CRC校验
 * @param data 数据
 * @param length 长度
 * @return CRC校验值
 *******************************************************************************/
uint16_t modbusCRC(const uint8_t *data, uint16_t length)
{
    uint16_t crc = 0xFFFF;

    for (uint16_t i = 0; i < length; i++)
    {
        crc ^= (uint16_t)data[i];
        for (uint8_t j = 8; j != 0; j--)
        {
            if ((crc & 0x0001) != 0)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
                crc >>= 1;
        }
    }

    return crc;
}
/** ****************************************************************************
 * @brief 内存动态分配
 * @param buffer 内存指针
 * @param pduSize 0-释放内存 1-分配内存
 * @param context 上下文信息
 * @return 处理结果
 *******************************************************************************/
int modbusBufferAllocateADU(ModbusBuffer *buffer, uint16_t pduSize, void *context)
{
    int err = MODBUS_OK;
    uint16_t size = pduSize;
    if (pduSize)size += buffer->padding;

    if(buffer->allocator)   //没有allocator说明是静态分配
    {
        int err = buffer->allocator(buffer, size, context);
        if (err == MODBUS_ERR_ALLOC || size == 0)
        {
            buffer->data = NULL;
            buffer->pdu = NULL;
            buffer->len = 0;
            return err;
        }
    }
    buffer->pdu  = buffer->data + buffer->pduOffset;
    buffer->len  = size;
    buffer->plen = pduSize;
    return err;
}
/** ****************************************************************************
 * @brief  报文缓冲区初始化
 *******************************************************************************/
void Modbus_BufferInit(MbusMode_t mode,ModbusBuffer *buf)
{
    switch (mode)
    {
    case MODBUS_PDU:{buf->padding = 0                     ;buf->pduOffset = 0                    ;};break;
    case MODBUS_RTU:{buf->padding = MODBUS_RTU_ADU_PADDING;buf->pduOffset = MODBUS_RTU_PDU_OFFSET;};break;
    case MODBUS_TCP:{buf->padding = MODBUS_TCP_ADU_PADDING;buf->pduOffset = MODBUS_RTU_PDU_OFFSET;};break;
    default:break;
    }
}

/** ****************************************************************************
 * @brief RTU报文打包
 * @param frame 报文指针
 * @param length 报文长度(4 - 256)
 * @param address 从机地址
 * @return 操作结果
 *******************************************************************************/
int modbusPackRTU(uint8_t *frame, uint16_t length, uint8_t address)
{
    // 检查长度
    if (length < MODBUS_RTU_ADU_MIN || length > MODBUS_RTU_ADU_MAX)
        return MODBUS_ERR_LEN;
    // 写入地址
    frame[0] = address;
    // 计算和写入CRC
    modbusWLE(&frame[length - 2], modbusCRC(frame, length - 2));

    return MODBUS_OK;
}
/** ****************************************************************************
 * @brief TCP报文打包
 * @param frame 报文指针
 * @param length 报文长度(4 - 256)
 * @param transactionID 协议标识符
 * @param unitID 单元标识符
 * @return 操作结果
 *******************************************************************************/
int modbusPackTCP(uint8_t *frame, uint16_t length, uint16_t transactionID, uint8_t unitID)
{
    // 检查长度
    if (length < MODBUS_TCP_ADU_MIN || length > MODBUS_TCP_ADU_MAX)
        return MODBUS_ERR_LEN;

    modbusWBE(&frame[0], transactionID);
    modbusWBE(&frame[2], 0);
    modbusWBE(&frame[4], length - 6);
    frame[6] = unitID;
    return MODBUS_OK;
}
/** ****************************************************************************
 * @brief 报文打包
 * @param frame 报文指针
 * @param len 报文长度(4 - 256)
 * @return 操作结果
 *******************************************************************************/
int modbusPack(ModbusMaster_t *obj,ModbusBuffer* buf)
{
    switch (obj->mode)
    {
    case MODBUS_PDU:return MODBUS_OK;
    case MODBUS_RTU:
    {
        buf->addr = obj->addr;
        return modbusPackRTU(buf->data,buf->len,buf->addr);
    }
    case MODBUS_TCP:
    {
        buf->txId   = obj->txId++;
        buf->unitId = obj->unitId;
        return modbusPackTCP(buf->data,buf->len,buf->txId, buf->unitId);
    }
    default:return -1;
    }
}
/** ****************************************************************************
 * @brief RTU报文解析
 * @param frame 报文指针
 * @param length 报文长度(4 - 256)
 * @param checkCRC 校验CRC
 * @param pdu pdu数据指针
 * @param pduLength pdu数据长度
 * @param address 从机地址
 * @return 操作结果
 *******************************************************************************/
int modbusUnpackRTU(const uint8_t *frame, uint16_t flen, uint8_t checkCRC, const uint8_t **pdu, uint16_t *plen, uint8_t *addr)
{
    // 检查长度
    if (flen < MODBUS_RTU_ADU_MIN || flen > MODBUS_RTU_ADU_MAX)
        return MODBUS_ERR_LEN;
    // 提起地址
    *addr = frame[0];
    // 校验CRC
    if (checkCRC && modbusCRC(frame, flen - 2) != modbusRLE(frame + flen - 2))
        return MODBUS_ERR_CRC;
    // 报文信息提取
    *pdu = frame + MODBUS_RTU_PDU_OFFSET;
    *plen = flen - MODBUS_RTU_ADU_PADDING;

    return MODBUS_OK;
}
/** ****************************************************************************
 * @brief TCP报文解析
 * @param frame 报文指针
 * @param length 报文长度(4 - 256)
 * @param pdu pdu数据指针
 * @param pduLength pdu数据长度
 * @param transactionID 协议标识符
 * @param unitID 单元标识符
 * @return 操作结果
 *******************************************************************************/
int modbusUnpackTCP(const uint8_t *frame, uint16_t flen, const uint8_t **pdu, uint16_t *plen, uint16_t *txId, uint8_t *unitID)
{
    // 检查长度
    if (flen < MODBUS_TCP_ADU_MIN || flen > MODBUS_TCP_ADU_MAX)
        return MODBUS_ERR_LEN;
    // 报文类型检查
    uint16_t protocolID = modbusRBE(&frame[2]);
    if (protocolID != 0)
        return MODBUS_ERR_PROTOCOL;
    // 长度检查
    uint16_t messageLength = modbusRBE(&frame[4]);
    if (messageLength != flen - 6)
        return MODBUS_ERR_LEN;
    // 报文信息提取
    *txId = modbusRBE(&frame[0]);
    *unitID = frame[6];
    *pdu = frame + MODBUS_TCP_PDU_OFFSET;
    *plen = flen - MODBUS_TCP_ADU_PADDING;

    return MODBUS_OK;
}


/*------------------------------------------------------------------------------
主机部分
------------------------------------------------------------------------------*/

/** ****************************************************************************
 * @brief  解析DPU响应报文
 * @param status 设备状态
 * @param address 从机地址
 * @param request 请求PDU报文
 * @param len 请求PDU报文长度
 * @param response 响应PDU报文
 * @param responseLength 相应PDU报文长度
 * @return 初始化结果
 *******************************************************************************/
int modbusParseResponsePDU(ModbusMaster_t *obj, uint16_t* data, uint16_t cnt)
{
    // 检查长度是否合适
    if (!obj->send.plen || obj->send.plen > MODBUS_PDU_MAX)
        return MODBUS_ERR_LEN;
    if (!obj->recv.plen || obj->recv.plen > MODBUS_PDU_MAX)
        return MODBUS_ERR_LEN;

    uint8_t function = obj->recv.pdu[0];

    // 处理异常帧
    if ( (function & 0x80) && obj->recv.plen == 2)
    {
        return -obj->recv.pdu[1];
    }

    // 检查功能码是否与请求帧中的功能码匹配
    if (function != obj->send.pdu[0])
        return MODBUS_ERR_FUNC;

    // 查找解析函数
    for (uint16_t i = 0; i < obj->funcNum; i++)
    {
        if (function == obj->funcList[i].id)
        {
            return obj->funcList[i].response(obj,function,0,data,cnt);
        }
    }
    // 没有匹配的函数处理程序
    return MODBUS_ERR_FUNC;
}

/** ****************************************************************************
 * @brief  解析RTU响应报文
 * @param status 设备状态
 * @param request 请求RTU报文
 * @param len 请求RTU报文长度
 * @param response 响应RTU报文
 * @param responseLength 相应RTU报文长度
 * @return 初始化结果
 *******************************************************************************/
int modbusParseResponseRTU(ModbusMaster_t *obj, uint16_t* data, uint16_t cnt)
{
    // 解析响应
    int err = modbusUnpackRTU(
        obj->recv.data,
        obj->recv.len,
        1,
        (const uint8_t **)&(obj->recv.pdu),
        &obj->recv.plen,
        &obj->recv.addr);

    if (err != MODBUS_OK)return err;

    // 检查地址-不处理广播请求的响应和错误的响应地址
    if (obj->recv.addr == 0 || obj->recv.addr != obj->send.addr)
        return MODBUS_ERR_ADDR;

    // 解析PDU本身
    return modbusParseResponsePDU(obj,data,cnt);
}

/** ****************************************************************************
 * @brief  解析TCP响应报文
 * @param status 设备状态
 * @param request 请求TCP报文
 * @param len 请求TCP报文长度
 * @param response 响应RTU报文
 * @param responseLength 相应TCP报文长度
 * @return 初始化结果
 *******************************************************************************/
int modbusParseResponseTCP(ModbusMaster_t *obj, uint16_t* data, uint16_t cnt)
{
    // 解析响应
    int err = modbusUnpackTCP(
        obj->recv.data,
        obj->recv.len,
        (const uint8_t **)&(obj->recv.pdu),
        &obj->recv.plen,
        &obj->recv.txId,
        &obj->recv.unitId);

    if (err != MODBUS_OK)return err;

    // 检查事务标识符是否匹配
    if (obj->recv.txId != obj->send.txId )
        return MODBUS_ERR_TRANSACTION; 

    // 检测单元标识符是否匹配
    if (obj->recv.unitId != obj->send.unitId)
        return MODBUS_ERR_ADDR;

    // 解析PDU本身
    return modbusParseResponsePDU(obj,data,cnt);
}
/** ****************************************************************************
 * @brief 解析响应报文
 * @param status 设备状态
 * @param request 请求报文
 * @param len 请求报文长度
 * @param response 响应报文
 * @param responseLength 相应报文长度
 * @return 初始化结果
 *******************************************************************************/
int Modbus_ParseResponse(ModbusMaster_t *obj, uint16_t* data, uint16_t cnt)
{
    switch (obj->mode)
    {
    case MODBUS_PDU:return modbusParseResponsePDU(obj,data,cnt);
    case MODBUS_RTU:return modbusParseResponseRTU(obj,data,cnt);
    case MODBUS_TCP:return modbusParseResponseTCP(obj,data,cnt);
    default:return MODBUS_ERR_OTHER;
    }
}
/** ****************************************************************************
 * @brief 01 02 03 04 请求报文生成
 * @param status 主机设备状态
 * @param type 寄存器类型
 * @param index 寄存器序号
 * @param count 寄存器数量
 * @return 处理结果
 *******************************************************************************/
int modbusBuildRequest01020304(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
    ModbusMaster_t* obj = (ModbusMaster_t*)master;
    uint16_t maxCount;
    switch (func)
    {
    case 1:
    case 2:maxCount = 2000;break;
    case 3:
    case 4:maxCount = 125;break;
    default:return MODBUS_ERR_FUNC;
    }

    // Check count
    if (cnt == 0 || cnt > maxCount)
        return MODBUS_ERR_COUNT;

    // Address range check
    if (idx > UINT16_MAX - cnt + 1)
        return MODBUS_ERR_RANGE;

    if (modbusBufferAllocateADU(&obj->send, 5, obj->cxt))
        return MODBUS_ERR_ALLOC;

    obj->send.pdu[0] =func;
    modbusWBE(&obj->send.pdu[1], idx);
    modbusWBE(&obj->send.pdu[3], cnt);

    return MODBUS_OK;
}
/** ****************************************************************************
 * @brief 05 06请求报文生成
 * @param status 主机设备状态
 * @param type 寄存器类型
 * @param index 寄存器序号
 * @param count 寄存器数量
 * @return 处理结果
 *******************************************************************************/
int modbusBuildRequest0506(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
    ModbusMaster_t* obj = (ModbusMaster_t*)master;
    uint16_t val;
    if (func != 5 && func != 6)
        return MODBUS_ERR_FUNC;

    // 使用适当的值写入线圈
    if (func == 5)
    {
        val = *(uint8_t*)reg ? 0xff00 : 0;
    }
    else
    {
        val = *(uint16_t*)reg;
    }
        
    if (modbusBufferAllocateADU(&obj->send, 5, obj->cxt))
        return MODBUS_ERR_ALLOC;

    obj->send.pdu[0] = func;
    modbusWBE(&obj->send.pdu[1], idx);
    modbusWBE(&obj->send.pdu[3], val);

    return MODBUS_OK;
}
/** ****************************************************************************
 * @brief 15请求报文生成
 * @param status 主机设备状态
 * @param index 寄存器序号
 * @param count 寄存器数量
 * @param values 内容指针
 * @return 处理结果
 *******************************************************************************/
int modbusBuildRequest15(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
    ModbusMaster_t* obj = (ModbusMaster_t*)master;
    // 寄存器数量检查
    if (cnt == 0 || cnt > 1968)
        return MODBUS_ERR_COUNT;

    // 地址范围检查
    if (idx > UINT16_MAX - cnt + 1)
        return MODBUS_ERR_RANGE;

    uint8_t dataLength = MODBUS_BITS_TO_BYTES(cnt);

    if (modbusBufferAllocateADU(&obj->send, 6 + dataLength, obj->cxt))
        return MODBUS_ERR_ALLOC;

    // 完整字节数和剩余位
    uint8_t n = cnt >> 3;
    uint8_t r = cnt & 7;

    // 拷贝n个完整字节
    for (uint8_t i = 0; i < n; i++)obj->send.pdu[6 + i] = *((uint8_t*)reg+i);

    // 拷贝剩余位
    if (r)
    {
        obj->send.pdu[6 + n] = 0;
        for (uint8_t i = 0; i < r; i++)
        {
            modbusMaskWrite(&obj->send.pdu[6 + n],i,modbusMaskRead((uint8_t*)reg+n, i));
        }
    }
    obj->send.pdu[0] = 15;
    modbusWBE(&obj->send.pdu[1], idx);
    modbusWBE(&obj->send.pdu[3], cnt);
    obj->send.pdu[5] = dataLength;

    return MODBUS_OK;
}
/** ****************************************************************************
 * @brief 16请求报文生成
 * @param status 主机设备状态
 * @param index 寄存器序号
 * @param count 寄存器数量
 * @param values 内容指针
 * @return 处理结果
 *******************************************************************************/
int modbusBuildRequest16(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
    ModbusMaster_t* obj = (ModbusMaster_t*)master;
    // 寄存器数量检查
    if (cnt == 0 || cnt > 123)
        return MODBUS_ERR_COUNT;

    // 地址范围检查
    if (idx > UINT16_MAX - cnt + 1)
        return MODBUS_ERR_RANGE;

    uint8_t dataLength = cnt << 1;

    if (modbusBufferAllocateADU(&obj->send, 6 + dataLength, obj->cxt))
        return MODBUS_ERR_ALLOC;

    // 拷贝寄存器值
    for (uint8_t i = 0; i < (uint8_t)cnt; i++)
        modbusWBE(&obj->send.pdu[6 + (i << 1)], *((uint16_t*)reg+i));
    // 写入报文头
    obj->send.pdu[0] = 16;
    modbusWBE(&obj->send.pdu[1], idx);
    modbusWBE(&obj->send.pdu[3], cnt);
    obj->send.pdu[5] = dataLength;
    return MODBUS_OK;
}
/** ****************************************************************************
* @brief  01 02 03 04 报文响应解析
* @param obj  主机对象
* @param func 功能代码
* @param data 数据buf指针
* @param cnt 寄存器数量
* @return 操作结果
*******************************************************************************/
int modbusParseResponse01020304(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
	ModbusMaster_t* obj = (ModbusMaster_t*)master;
    // 检查长度是否合适
	if (obj->send.plen != 5) return MODBUS_ERR_LEN;
	if (obj->recv.plen  < 3) return MODBUS_ERR_LEN;

	// 确定数据类型
	uint8_t bits;
	uint16_t maxCount;
	switch (func)
	{
		case 1: {maxCount=2000; bits=1 ;}break;
		case 2: {maxCount=2000; bits=1 ;}break;
		case 3: {maxCount=125 ; bits=16;}break;
		case 4: {maxCount=125 ; bits=16;}break;
		default:return MODBUS_ERR_FUNC;
	}

	// uint16_t index = modbusRBE(&obj->recv.pdu[1]);
	// uint16_t count = modbusRBE(&obj->recv.pdu[3]);

	// 检查寄存器数量
	if (cnt == 0 || cnt > maxCount)
		return MODBUS_ERR_COUNT;

	// 地址范围检查
	if (idx > UINT16_MAX - cnt + 1)
		return MODBUS_ERR_RANGE;

	// 根据请求，计算预期数据大小
	uint8_t expected = (bits == 16) ? (cnt << 1) : MODBUS_BITS_TO_BYTES(cnt);

	// 检查声明的数据大小是否匹配以及响应长度是否有效
	if (obj->recv.pdu[1] != expected || (obj->recv.plen != expected + 2))
		return MODBUS_ERR_LEN;

	// 最后从响应中读取数据
	for (uint16_t i = 0; i < cnt; i++)
	{
		if (bits == 1)
			*((uint8_t*)reg+i) = obj->recv.pdu[2+i];
		else
			*((uint16_t*)reg+i) = modbusRBE(&obj->recv.pdu[2 + (i << 1)]);
	}
	return MODBUS_OK;
}

/** ****************************************************************************
* @brief  05 06 报文响应解析
* @param obj  主机对象
* @param func 功能代码
* @param data 数据buf指针
* @param cnt 寄存器数量
* @return 返回PDU报文
*******************************************************************************/
int modbusParseResponse0506(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
	ModbusMaster_t* obj = (ModbusMaster_t*)master;
    // 检查长度是否合适
	if (obj->send.plen != 5) return MODBUS_ERR_LEN;
	if (obj->recv.plen != 5) return MODBUS_ERR_LEN;

	// 寄存器地址校验
	if (modbusRBE(&obj->send.pdu[1]) != modbusRBE(&obj->recv.pdu[1]))
		return MODBUS_ERR_INDEX;

	// 寄存器值校验
	if (modbusRBE(&obj->send.pdu[3]) != modbusRBE(&obj->recv.pdu[3]))
		return MODBUS_ERR_VALUE;

	return MODBUS_OK;
}

/** ****************************************************************************
* @brief  05 06 报文响应解析
* @param status 主机设备状态
* @param address 从机地址
* @param function 功能代码
* @param pdu 请求PDU报文指针
* @param len 请求PDU报文长度
* @param responsePDU 响应PDU报文指针
* @param responseLength 响应PDU报文长度
* @return 返回PDU报文
*******************************************************************************/
int modbusParseResponse1516(void *master,uint8_t func,uint16_t idx,void *reg,uint16_t cnt)
{
	ModbusMaster_t* obj = (ModbusMaster_t*)master;
    // 检查长度是否合适
	if (obj->send.plen  < 7) return MODBUS_ERR_LEN;
	if (obj->recv.plen != 5) return MODBUS_ERR_LEN;

	uint16_t index = modbusRBE(&obj->send.pdu[1]);
	uint16_t count = modbusRBE(&obj->send.pdu[3]);

	// 寄存器地址校验
	if (index != modbusRBE(&obj->recv.pdu[1]))
		return MODBUS_ERR_INDEX;
		
	// 寄存器数量校验
	if (count != modbusRBE(&obj->recv.pdu[3]))
		return MODBUS_ERR_COUNT;

	// 最大寄存器数量校验
	uint16_t maxCount = (func == 15) ? 1968 : 123;
	if (count == 0 || count > maxCount)
		return MODBUS_ERR_COUNT;
	
	// 验证请求长度是否正确
	uint16_t expected = func == 15 ? MODBUS_BITS_TO_BYTES(count) : (count << 1);
	if (obj->send.plen != expected + 6)
		return MODBUS_ERR_RANGE;

	// 验证寄存器范围
	if (index > UINT16_MAX - count + 1)
		return MODBUS_ERR_RANGE;
	
	return MODBUS_OK;
}
/*******************************************************************************
 * @brief 获取报文长度
 *******************************************************************************/
int Modbus_MasterGetResponseLen(ModbusMaster_t *obj, uint8_t func,uint16_t cnt)
{
    uint16_t len=0;
    switch (func)
    {
    case 1  : 
    case 2  : len = MODBUS_BITS_TO_BYTES(cnt)+2;break;
    case 3  : 
    case 4  : len = (cnt << 1) + 2;break;
    case 5  : 
    case 6  : 
    case 15 : 
    case 16 : len = 5;break;
    default:break;
    }

    return len;
}
/*******************************************************************************
 * @brief 主机功能码执行
 * @param obj 协议对象
 * @param type 寄存器类型
 * @param addr 寄存器地址
 * @param buf  缓存地址
 * @param cnt  寄存器数量
 *******************************************************************************/
ModbusInfo_t Modbus_MasterFunc(ModbusMaster_t *obj, uint8_t func, uint16_t idx, uint16_t cnt,uint16_t* reg)
{
    ModbusInfo_t res = MODBUS_OK;
    // 发送请求
    if (!obj->isSent)
    {
        Modbus_BufferInit(obj->mode,&obj->send);                            //初始化报文

        for(int i=0;i<obj->funcNum;i++)
        {
            if(func == obj->funcList[i].id)
            {
                res = (ModbusInfo_t)obj->funcList[i].request(obj,func,idx,reg,cnt);break;    //按类型填充报文内容
            }
        }
        if(res != MODBUS_OK)return res;

        res = (ModbusInfo_t)modbusPack(obj,&obj->send);                                   //报文打包 
        if(res != MODBUS_OK)return res;  

        res = (ModbusInfo_t)obj->port.send(obj->cxt,obj->send.data,obj->send.len);        //报文发送
        if(res != MODBUS_OK)return res;
        // 接收预处理
        Modbus_BufferInit(obj->mode,&obj->recv);
        obj->recv.len = Modbus_MasterGetResponseLen(obj,func,cnt);
        if(modbusBufferAllocateADU(&obj->recv, obj->recv.len, obj->cxt))return MODBUS_ERR_ALLOC;
        obj->isSent = res == MODBUS_OK ? true : false;
    }
    // 接收响应
    if(obj->isSent)
    {   
        res = (ModbusInfo_t)obj->port.recv(obj->cxt,obj->recv.data,obj->recv.len); // 是否阻塞由该函数决定
        if(res > 0)
        {
            obj->isSent = false;
            obj->recv.len = res;
            return (ModbusInfo_t)Modbus_ParseResponse(obj,reg,cnt);
        }
        else if(res < 0)
        {
            obj->isSent = false;    //接收错误
            return MODBUS_EXCEP_NACK;
        }
        return MODBUS_RECV_WAIT;
        //接收中不做处理
    }
    return res;
}
/*******************************************************************************
 * @brief 主机写
 * @param obj 协议对象
 * @param type 寄存器类型
 * @param addr 寄存器地址
 * @param buf  数据缓存地址
 * @param cnt  寄存器数量
 *******************************************************************************/
ModbusInfo_t Modbus_MasterWrite(ModbusMaster_t *obj, ModbusDataType_t type, uint16_t idx, uint16_t cnt,uint16_t* reg)
{
    uint8_t func;
    if (type != MODBUS_REG_0x && type != MODBUS_REG_4x)return MODBUS_ERR_FUNC;

    if (cnt > 1)
    {
        switch (type)
        {
        case MODBUS_REG_0x:func=15;break;
        case MODBUS_REG_4x:func=16;break;
        default:return MODBUS_ERR_FUNC;
        }
    }
    else
    {
        func = type == MODBUS_REG_0x ? 5 : 6;
        
    }
    return Modbus_MasterFunc(obj,func,idx,cnt,reg);
}
/*******************************************************************************
 * @brief 主机读
 * @param obj 协议对象
 * @param type 寄存器类型
 * @param addr 寄存器地址
 * @param buf  缓存地址
 * @param cnt  寄存器数量
 *******************************************************************************/
ModbusInfo_t Modbus_MasterRead(ModbusMaster_t *obj, ModbusDataType_t type, uint16_t idx, uint16_t cnt,uint16_t* reg)
{
    uint8_t func;
    switch (type)
    {
    case MODBUS_REG_0x: func = 1;break;
    case MODBUS_REG_1x: func = 2;break;
    case MODBUS_REG_4x: func = 3;break;
    case MODBUS_REG_3x: func = 4;break;
    default:return MODBUS_ERR_FUNC;
    }
    return Modbus_MasterFunc(obj,func,idx,cnt,reg);
}

/*******************************************************************************
 * @brief 初始化
 *******************************************************************************/
ModbusInfo_t Modbus_MasterFuncSet(ModbusMaster_t *obj,uint8_t id, modusFunc_t request,modusFunc_t response)
{
    int i,temp=-1;
    //修改功能码
    for(i=0;i<MODBUS_MASTER_FUNC_DEF_NUM + MODBUS_MASTER_FUNC_EXT_NUM;i++)
    {
        if(obj->funcList[i].id == id)
        {
            obj->funcList[i].request  = request;
            obj->funcList[i].response = response;
            return MODBUS_OK;
        }
        else if (temp == -1 && obj->funcList[i].id ==0)
        {
            temp = i;
        }
    }
    //增加新的功能码
    if(i == MODBUS_MASTER_FUNC_DEF_NUM + MODBUS_MASTER_FUNC_EXT_NUM && temp!=-1)
    {
        obj->funcList[temp].id = id;
        obj->funcList[temp].request  = request;
        obj->funcList[temp].response = response;
        obj->funcNum++;
        return MODBUS_OK;
    }
    return MODBUS_ERR_FUNC;
}
/*******************************************************************************
 * @brief 初始化
 * @param obj master对象
 * @param mode 通讯模式
 * @param addrOrId 从机地址或者单元标识符
 *******************************************************************************/
ModbusInfo_t Modbus_MasterInit(ModbusMaster_t *obj,MbusMode_t mode,uint8_t addrOrId)
{
    obj->mode = mode;
    switch (obj->mode)
    {
    case MODBUS_RTU: obj->addr   = addrOrId;break;
    case MODBUS_TCP: obj->unitId = addrOrId;break;
    default:break;
    }
    obj->isSent = NULL;
    obj->funcNum = 0;
    Modbus_MasterFuncSet(obj,1 ,modbusBuildRequest01020304,modbusParseResponse01020304);
    Modbus_MasterFuncSet(obj,2 ,modbusBuildRequest01020304,modbusParseResponse01020304);
    Modbus_MasterFuncSet(obj,3 ,modbusBuildRequest01020304,modbusParseResponse01020304);
    Modbus_MasterFuncSet(obj,4 ,modbusBuildRequest01020304,modbusParseResponse01020304);
    Modbus_MasterFuncSet(obj,5 ,modbusBuildRequest0506    ,modbusParseResponse0506    );
    Modbus_MasterFuncSet(obj,6 ,modbusBuildRequest0506    ,modbusParseResponse0506    );
    Modbus_MasterFuncSet(obj,15,modbusBuildRequest15      ,modbusParseResponse1516    );
    Modbus_MasterFuncSet(obj,16,modbusBuildRequest16      ,modbusParseResponse1516    );  

    return MODBUS_OK;
}

/*------------------------------------------------------------------------------
从机部分
------------------------------------------------------------------------------*/
/** ****************************************************************************
* @brief  01 02 03 04 功能码响应
* @param status 设备状态
* @param function 功能码
* @param pdu 响应PDU报文
* @param len 报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequest01020304(void *slave,uint8_t func,uint16_t idx, void *rpdu,uint16_t len)
{
    ModbusSlave_t *obj = (ModbusSlave_t *)slave;
    const uint8_t* pdu = (const uint8_t*)rpdu;
    uint16_t reg_val;
    int res;
    // 帧长度检查
	if (len != 5)return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

	ModbusDataType_t datatype;
	uint16_t maxCount;
	uint8_t isCoilType;
	switch (func)
	{
    case 1: datatype = MODBUS_REG_0x; maxCount = 2000; isCoilType = 1; break;
    case 2: datatype = MODBUS_REG_1x; maxCount = 2000; isCoilType = 1; break;
    case 3: datatype = MODBUS_REG_4x; maxCount =  125; isCoilType = 0; break;
    case 4: datatype = MODBUS_REG_3x; maxCount =  125; isCoilType = 0; break;
    default:return MODBUS_ERR_FUNC;break;
	}

	uint16_t index = modbusRBE(&pdu[1]);
	uint16_t count = modbusRBE(&pdu[3]);

	// 检查数量
	if (count == 0 || count > maxCount)
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

	// 地址范围检查
	if (index > UINT16_MAX - count + 1)
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_ADDRESS);

	// 生成正常响应报文
	uint8_t dataLength = (isCoilType ? MODBUS_BITS_TO_BYTES(count) : (count << 1));
	if (modbusBufferAllocateADU(&obj->send, 2 + dataLength,obj->cxt))
		return MODBUS_ERR_ALLOC;

	obj->send.pdu[0] = func;
	obj->send.pdu[1] = dataLength;
	
	// 写入前清空寄存器,避免写入出错
	for (uint8_t i = 0; i < dataLength; i++)obj->send.pdu[2 + i] = 0;

	for (uint16_t i = 0; i < count; i++)
	{
        res = obj->readCallback(obj->cxt,datatype,index + i,&reg_val);
        if(res) 
        {
            modbusBufferAllocateADU(&obj->send, 0,obj->cxt);
            return modbusBuildException(obj, func, res);
        }
		if (isCoilType)
			modbusMaskWrite(&obj->send.pdu[2], i, reg_val != 0);
		else
			modbusWBE(&obj->send.pdu[2 + (i << 1)], reg_val);
	}

	return MODBUS_OK;
}

/** ****************************************************************************
* @brief  05 06 功能码响应
* @param status 设备状态
* @param function 功能码
* @param pdu 响应PDU报文
* @param len 报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequest0506(void *slave,uint8_t func,uint16_t idx, void *rpdu,uint16_t len)
{
    ModbusSlave_t *obj = (ModbusSlave_t *)slave;
    const uint8_t* pdu = (const uint8_t*)rpdu;
    // 长度检查
	if (len != 5)return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

	// 获取寄存器起始地址和值
	ModbusDataType_t datatype = func == 5 ? MODBUS_REG_0x : MODBUS_REG_4x;
	uint16_t index = modbusRBE(&pdu[1]);
	uint16_t value = modbusRBE(&pdu[3]);

	// 对于线圈-检查线圈值是否有效
	if (datatype == MODBUS_REG_0x && value != 0x0000 && value != 0xFF00)
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

    //写入线圈/寄存器
    //请记住，当强制转换为uint8_t时，0xff00为0
    int res = obj->writeCallback(obj->cxt,datatype,index,value);
    if(res) return modbusBuildException(obj, func, res);

	// 生成正常响应报文
	if (modbusBufferAllocateADU(&obj->send, 5,obj->cxt))
		return MODBUS_ERR_ALLOC;

	obj->send.pdu[0] = func;
	modbusWBE(&obj->send.pdu[1], index);
	modbusWBE(&obj->send.pdu[3], value);

	return MODBUS_OK;
}

/** ****************************************************************************
* @brief  15 16 功能码响应
* @param status 设备状态
* @param function 功能码
* @param pdu 响应PDU报文
* @param len 报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequest1516(void *slave,uint8_t func,uint16_t idx, void *rpdu,uint16_t len)
{
    ModbusSlave_t *obj = (ModbusSlave_t *)slave;
    const uint8_t* pdu = (const uint8_t*)rpdu;
    uint16_t reg_idx,reg_val;
    int res;
    // 长度检查
	if (len < 6)
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

	// 获取寄存器起始地址和长度
	ModbusDataType_t datatype = func == 15 ? MODBUS_REG_0x : MODBUS_REG_4x;
	uint16_t maxCount = datatype == MODBUS_REG_0x ? 1968 : 123;
	uint16_t index = modbusRBE(&pdu[1]);
	uint16_t count = modbusRBE(&pdu[3]);
	uint8_t declaredLength = pdu[5];

	// 检查声明的长度是否正确
	if (declaredLength == 0 || declaredLength != len - 6)
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

	// 检查数量
	if (count == 0
		|| count > maxCount
		|| declaredLength != (datatype == MODBUS_REG_0x ? MODBUS_BITS_TO_BYTES(count) : (count << 1)))
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_VALUE);

	// 地址范围检查
	if (index > UINT16_MAX - count + 1)
		return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_ADDRESS);

	// 写入访问
	for (uint16_t i = 0; i < count; i++)
	{
        reg_idx = index + i;
		reg_val = datatype == MODBUS_REG_0x ? modbusMaskRead(&pdu[6], i) : modbusRBE(&pdu[6 + (i << 1)]);
        res = obj->writeCallback(obj->cxt,datatype,reg_idx,reg_val);
		if(res) return modbusBuildException(obj, func, res);
	}

	// 生成正常响应报文
	if (modbusBufferAllocateADU(&obj->send, 5,obj->cxt))return MODBUS_ERR_ALLOC;

	obj->send.pdu[0] = func;
	modbusWBE(&obj->send.pdu[1], index);
	modbusWBE(&obj->send.pdu[3], count);	

	return MODBUS_OK;
}
/** ****************************************************************************
* @brief  创建异常响应报文
* @param status 从机设备状态
* @param function 功能码
* @param code 异常代码
* @return 处理结果
*******************************************************************************/
int modbusBuildException(ModbusSlave_t *obj,uint8_t func,int code)
{
	if (modbusBufferAllocateADU(&obj->send, 2,obj->cxt))return MODBUS_ERR_ALLOC;

	obj->send.pdu[0] = func | 0x80;
	obj->send.pdu[1] = -code;

	return MODBUS_OK;
}
/** ****************************************************************************
* @brief  请求报文解析
* @param status 从机设备状态
* @param request 请求报文
* @param len 请求报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequest(ModbusSlave_t *obj, const uint8_t *pdu, uint8_t plen)
{
	uint8_t func = pdu[0];

	// 查找匹配函数
	for (uint16_t i = 0; i < obj->funcNum; i++)
    {
		if (func == obj->funcList[i].id)return obj->funcList[i].response(obj,func,0,(void*)pdu,plen);
    }

	// No match found
	return modbusBuildException(obj, func, MODBUS_EXCEP_ILLEGAL_FUNCTION);
}
/** ****************************************************************************
* @brief  PDU请求报文解析
* @param status 从机设备状态
* @param request 请求报文
* @param len 请求报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequestPDU(ModbusSlave_t *obj,const uint8_t* recv,uint16_t len)
{
	// 检查长度
	if (!len || len > MODBUS_PDU_MAX)return MODBUS_ERR_LEN;

	obj->send.padding = 0;
	obj->send.pduOffset = 0;
	return modbusParseRequest(obj,recv,len);
}
/** ****************************************************************************
* @brief  RTU请求报文解析
* @param obj 从机设备状态
* @param slaveAddress 从机地址
* @param request 请求报文
* @param len 请求报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequestRTU(ModbusSlave_t *obj,const uint8_t* recv,uint16_t len)
{
    // 解包请求
	int err = modbusUnpackRTU(
        recv,
        len,
		1,
        (const uint8_t **)&(obj->recv.pdu),
        &obj->recv.plen,
        &obj->recv.addr);

	if (err != MODBUS_OK)return err;

	// 校验帧是否是发给我们的
	if (obj->recv.addr != 0 && obj->recv.addr != obj->addr)
		return MODBUS_ERR_ADDR;
    // 不响应广播请求
    if (obj->recv.addr == 0)return MODBUS_OK;
	// 解析请求
    obj->send.padding = MODBUS_RTU_ADU_PADDING;
	obj->send.pduOffset = MODBUS_RTU_PDU_OFFSET;

    err = modbusParseRequest(obj, obj->recv.pdu, obj->recv.plen);
	if (err != MODBUS_OK)return err;
	
	if (obj->send.len)
	{
		// 打包响应帧
		return modbusPackRTU(obj->send.data,obj->send.len,obj->addr);
	}
	
	return MODBUS_OK;
}

/** ****************************************************************************
* @brief  TCP请求报文解析
* @param status 从机设备状态
* @param request 请求报文
* @param len 请求报文长度
* @return 处理结果
*******************************************************************************/
int modbusParseRequestTCP(ModbusSlave_t *obj,const uint8_t* recv,uint16_t len)
{
	// 解包请求
	int err = modbusUnpackTCP(
        recv,
        len,
        (const uint8_t **)&(obj->recv.pdu),
        &obj->recv.plen,
        &obj->recv.txId,
        &obj->recv.unitId);

    if (err != MODBUS_OK)return err;
	
	// 解析请求
	obj->send.padding   = MODBUS_TCP_ADU_PADDING;
	obj->send.pduOffset = MODBUS_TCP_PDU_OFFSET;

    err = modbusParseRequest(obj, obj->recv.pdu, obj->recv.plen);
	if (err != MODBUS_OK)return err;

	// 写入MBAP标头
	if (obj->send.len)
	{
		// 打包响应
		return modbusPackTCP(obj->send.data,obj->send.len,obj->recv.txId,obj->recv.unitId);
	}
	return MODBUS_OK;
}
/** ****************************************************************************
 * @brief 解析请求报文
 * @param status 设备状态
 * @param request 请求报文
 * @param len 请求报文长度
 * @param response 响应报文
 * @param responseLength 相应报文长度
 * @return 初始化结果
 *******************************************************************************/
int Modbus_ParseRequest(ModbusSlave_t *obj, uint8_t* recv, uint16_t len)
{
    int res = MODBUS_OK;
    switch (obj->mode)
    {
    case MODBUS_PDU:res = modbusParseRequestPDU(obj,recv,len);break;
    case MODBUS_RTU:res = modbusParseRequestRTU(obj,recv,len);break;
    case MODBUS_TCP:res = modbusParseRequestTCP(obj,recv,len);break;
    default:return MODBUS_ERR_OTHER;
    }
    
    if(res == MODBUS_OK)return obj->port.send(obj->cxt,obj->send.data,obj->send.len);
	return res;
}
/*******************************************************************************
 * @brief 获取报文长度
 *******************************************************************************/
int Modbus_SlaveGetFrameLen(MbusMode_t mode,uint8_t* frame)
{
	int len;
    //根据报文类型判断帧长度
	switch (frame[1])
	{
	case 1:
	case 2:
	case 3:
	case 4:
    case 5:
	case 6:	 len = 5;break;
    case 15: len = frame[6]+6;break;
	case 16: len = frame[6]+6;break;
	default: return 0;
	}

    switch (mode)
    {
    case MODBUS_PDU: break;
    case MODBUS_RTU: len += MODBUS_RTU_ADU_PADDING;break;
    case MODBUS_TCP: len += MODBUS_TCP_ADU_PADDING;break;
    default:return MODBUS_ERR_OTHER;
    }

    return len;
}
/*******************************************************************************
 * @brief 从机接收监听任务
 * @param obj master对象
 * @param mode 通讯模式
 * @param addrOrId 从机地址或者单元标识符
 *******************************************************************************/
ModbusInfo_t Modbus_SlaveTask(ModbusSlave_t *obj)
{
    int res = obj->port.recv(obj->cxt,obj->recv.data,obj->recv.len);
    if(res > 0)
    {
        obj->recv.len = res;
        return (ModbusInfo_t)Modbus_ParseRequest(obj,obj->recv.data,obj->recv.len);
    }
    return MODBUS_OK;
}
/*******************************************************************************
 * @brief 初始化
 *******************************************************************************/
ModbusInfo_t Modbus_SlaveFuncSet(ModbusSlave_t *obj,uint8_t id,modusFunc_t response)
{
    int i,temp=-1;
    //修改功能码
    for(i=0;i<MODBUS_SLAVE_FUNC_DEF_NUM + MODBUS_SLAVE_FUNC_EXT_NUM;i++)
    {
        if(obj->funcList[i].id == id)
        {
            obj->funcList[i].response = response;
            return MODBUS_OK;
        }
        else if (temp == -1 && obj->funcList[i].id ==0)
        {
            temp = i;
        }
    }
    //增加新的功能码
    if(i == MODBUS_SLAVE_FUNC_DEF_NUM + MODBUS_SLAVE_FUNC_EXT_NUM && temp!=-1)
    {
        obj->funcList[temp].id = id;
        obj->funcList[temp].response = response;
        obj->funcNum++;
        return MODBUS_OK;
    }
    return MODBUS_ERR_FUNC;
}
/*******************************************************************************
 * @brief 初始化
 * @param obj master对象
 * @param mode 通讯模式
 * @param addrOrId 从机地址或者单元标识符
 *******************************************************************************/
ModbusInfo_t Modbus_SlaveInit(ModbusSlave_t *obj,MbusMode_t mode,uint8_t addr)
{
    obj->mode = mode;
    obj->addr = addr;
    //添加默认功能码支持
    obj->funcNum = 0;
    Modbus_SlaveFuncSet(obj,1 ,modbusParseRequest01020304);
    Modbus_SlaveFuncSet(obj,2 ,modbusParseRequest01020304);
    Modbus_SlaveFuncSet(obj,3 ,modbusParseRequest01020304);
    Modbus_SlaveFuncSet(obj,4 ,modbusParseRequest01020304);
    Modbus_SlaveFuncSet(obj,5 ,modbusParseRequest0506    );
    Modbus_SlaveFuncSet(obj,6 ,modbusParseRequest0506    );
    Modbus_SlaveFuncSet(obj,15,modbusParseRequest1516    );
    Modbus_SlaveFuncSet(obj,16,modbusParseRequest1516    );  
    //申请接收内存
    switch (obj->mode)
    {
    case MODBUS_PDU: obj->recv.padding=                     0;obj->recv.pduOffset=                    0;break;
    case MODBUS_RTU: obj->recv.padding=MODBUS_RTU_ADU_PADDING;obj->recv.pduOffset=MODBUS_RTU_PDU_OFFSET;break;
    case MODBUS_TCP: obj->recv.padding=MODBUS_TCP_ADU_PADDING;obj->recv.pduOffset=MODBUS_TCP_PDU_OFFSET;break;
    default:return MODBUS_ERR_OTHER;
    }
    obj->recv.len = MODBUS_PDU_MAX;
    modbusBufferAllocateADU(&obj->recv,obj->recv.len,obj->cxt);
    return MODBUS_OK;
}
/*********************************END OF FILE**********************************/
